﻿#region Copyright(c) 1998-2012, Arnaud Colin Licence GNU GPL version 3
/* Copyright(c) 1998-2012, Arnaud Colin
 * All rights reserved.
 *
 * Licence GNU GPL version 3
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   -> Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   -> Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 */
#endregion

using System;
using System.Collections.Generic;
using System.Globalization;
#if NET35
using System.Linq;
#endif
using System.Reflection;
using System.Text;
using System.Diagnostics;
using XoLib.Compatibility;
using System.Runtime.CompilerServices;
//using System.Threading.Tasks;

namespace XoLib.Tools
{
    /// <summary>
    /// This class contains Version 
    /// </summary>
    [Serializable]
    public class CVersion : ICloneable, IComparable
    {
        private DateTime lastModified;      
        private int[] _Data = new int[4] {0,0,-1,-1};

        /// <summary>
        /// <para/>
        /// Static method get Version of the Master Process
        /// return a instance of the class
        /// <returns>Instance of the class.</returns>
        /// </summary>
         static public CVersion GetInfo()
        {
            CVersion ver = new CVersion();
#if !WindowsCE
            Process CurrentProcess = System.Diagnostics.Process.GetCurrentProcess();
            FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(CurrentProcess.Modules[0].FileName);
            System.IO.FileInfo fileInfo = new System.IO.FileInfo(fileVersionInfo.FileName);

            ver.lastModified = fileInfo.LastWriteTime;
            ver.Convert(fileVersionInfo.ProductVersion);
#else
            System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
             ver.lastModified = DateTime.MinValue;
             ver.Convert(Assembly.GetExecutingAssembly().GetName().Version);
#endif
            return ver;
        }


         /// <summary>
         /// <para/>
         /// ToString Verbose function
         /// <returns>"Version Q3 2012, released 26/07/2012"</returns>
         /// </summary>
        public string DateVerbose()
        {
            int quarter = (lastModified.Month / 3) + 1;
            return string.Format("Version Q{0} {1}, released {2:d2}/{3:d2}/{4}",
                    quarter, lastModified.Year, lastModified.Day, lastModified.Month, lastModified.Year);
        }


        /// <summary>
        /// <para/>
        /// Major
        /// <returns>int</returns>
        /// </summary>
        public int Major
        {
            get
            {
                return _Data[0];
            }
            set
            {
                _Data[0] = value;
            }
        }

        /// <summary>
        /// <para/>
        /// Minor
        /// <returns>int</returns>
        /// </summary>
        public int Minor
        {
            get
            {
                return _Data[1];
            }
            set
            {
                _Data[1] = value;
            }
        }

        /// <summary>
        /// <para/>
        /// Build
        /// <returns>int</returns>
        /// </summary>
        public int Build
        {
            get
            {
                return _Data[2];
            }
            set
            {
                _Data[2] = value;
            }
        }

        /// <summary>
        /// <para/>
        /// Revision
        /// <returns>int</returns>
        /// </summary>
        public int Revision
        {
            get
            {
                return _Data[3];
            }
            set
            {
                _Data[3] = value;
            }
        }

        /// <summary>
        /// <para/>
        /// Last time that the file whas Modified
        /// <returns>DateTime</returns>
        /// </summary>
        public DateTime LastModified
        {
            get
            {
                return lastModified;
            }
            set
            {
                lastModified = value;
            }
        }

        /// <summary>
        /// <para/>
        /// Construtor
        /// </summary>
        public CVersion()
        {
        }

#if WindowsCE
        /// <summary>
        /// <para>
        /// <param name="major"></param>
        /// <param name="minor"></param>
        /// </para>
        /// Construtor
        /// </summary>
        public CVersion(int major, int minor)
        {
            this.Major = major;
            this.Minor = minor;
            this.Build = -1;
            this.Revision = -1;
        }

        /// <summary>
        /// <para>
        /// <param name="major"></param>
        /// <param name="minor"></param>
        /// <param name="revision"></param>
        /// </para>
        /// Construtor
        /// </summary>
        public CVersion(int major, int minor, int build)
        {
            this.Major = major;
            this.Minor = minor;
            this.Build = build;
            this.Revision = -1;
        }
#endif

        /// <summary>
        /// <para>
        /// <param name="major"></param>
        /// <param name="minor"></param>
        /// <param name="build">Optional</param>
        /// <param name="revision">Optional</param>
        /// </para>
        /// <example> CVersion(5, 4, 10, 415)  CVersion(5, 4, 10)  CVersion(5, 4) </example>
        /// Construtor
        /// </summary>
#if !WindowsCE        
        public CVersion(int major, int minor, int build = -1, int revision = -1 )
#else
        public CVersion(int major, int minor, int build, int revision)
#endif
        {
            this.Major = major;
            this.Minor = minor;
            this.Build = build;
            this.Revision = revision;
        }

        /// <summary>
        /// <para>
        /// <param name="version"></param>
        /// </para>
        /// <example> CVersion("5.4.10.415") </example>
        /// Construtor
        /// </summary>
#if  NET45
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public CVersion(string version)
        {
            Convert(version);
        }

        /// <summary>
        /// <para>
        /// <param name="v1">System.Version</param>
        /// </para>
        /// Construtor
        /// </summary>
#if  NET45
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public CVersion(System.Version v1)
        {
            Convert(v1);
        }

        /// <summary>
        /// <para>
        /// <param name="v1">System.Version</param>
        /// </para>
        /// Convert System.Version in XoLib.CVersion
        /// <returns>Void</returns>
        /// </summary>
        public void Convert(System.Version v1)
        {
            this.Major = v1.Major;
            this.Minor = v1.Minor;
            this.Build = v1.Build;
            this.Revision = v1.Revision;
        }


        /// <summary>
        /// <para>
        /// <param name="obj">object</param>
        /// </para>
        /// Check if obj is Equal at CVersion
        /// <returns>bool</returns>
        /// </summary>
#if  NET45
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public override bool Equals(object obj)
        {
            return (CompareTo(obj) == 0);
        }


        /// <summary>
        /// Create a new instance with same data
        /// <returns>CVersion</returns>
        /// </summary>
        public object Clone()
        {
            CVersion version1 = new CVersion();
            this._Data.CopyTo(version1._Data, 0);
            version1.lastModified = this.lastModified;
            return version1;
        }


        /// <summary>
        /// <para>
        /// <param name="obj">object</param>
        /// </para>
        /// Compare obj with CVersion
        /// <example> new CVersion(4, 0, 10).CompareTo( new CVersion(1, 2) ) = 1 </example>/// 
        /// <returns>int</returns>
        /// </summary>
        public int CompareTo(object obj)
        {
            CVersion version = null;

            if (obj == null)
            {
                return 1;
            }
            if (!(obj is CVersion) && !(obj is System.Version) && !(obj is string))
            {
                throw new ArgumentException("Arg_TypeNotRecognized");
            }
            if (obj is System.Version)
            {
                version = new CVersion((System.Version)obj);
            }
            if (obj is CVersion)
            {
                version = (CVersion)obj; 
            }
            if (obj is string)
            {
                version = new CVersion((string)obj); 
            }

            for( int i =0; i< _Data.Length; i++)
            {
                if (this._Data[i] != version._Data[i])
                {
                    if (this._Data[i] > version._Data[i])
                    {
                        return 1;
                    }
                    return -1;
                }
            }
        
            return 0;
        }


        /// <summary>
        /// GetHashCode
        /// <returns>int</returns>
        /// </summary>
        public override int GetHashCode()
        {
            int num1 = 0;
            num1 |= ((this.Major & 15) << 0x1c);
            num1 |= ((this.Minor & 0xff) << 20);
            num1 |= ((this.Build & 0xff) << 12);
            return (num1 | this.Revision & 0xfff);
        }


        /// <summary>
        /// <para>
        /// <param name="v1">CVersion</param>
        /// <param name="v2">CVersion</param>
        /// </para>
        /// Compare v1 and v2 
        /// <example> new CVersion(4, 0, 10) == new CVersion(1, 2)  = false </example>/// 
        /// <returns>bool</returns>
        /// </summary>
        public static bool operator ==(CVersion v1, CVersion v2)
        {
            return v1.Equals(v2);
        }


        /// <summary>
        /// <para>
        /// <param name="v1">CVersion</param>
        /// <param name="v2">CVersion</param>
        /// </para>
        /// Compare if v1 superior at v2
        /// <example> new CVersion(4, 0, 10) > new CVersion(1, 2)  = true </example>/// 
        /// <returns>int</returns>
        /// </summary>
        public static bool operator >(CVersion v1, CVersion v2)
        {
            return (v2 < v1);
        }

        /// <summary>
        /// <para>
        /// <param name="v1">CVersion</param>
        /// <param name="v2">CVersion</param>
        /// </para>
        /// Compare if v1 superior and equal at v2
        /// <example> new CVersion(4, 0, 10) >= new CVersion(1, 2)  = true </example>/// 
        /// <returns>int</returns>
        /// </summary>
        public static bool operator >=(CVersion v1, CVersion v2)
        {
            return (v2 <= v1);
        }

        /// <summary>
        /// <para>
        /// <param name="v1">CVersion</param>
        /// <param name="v2">CVersion</param>
        /// </para>
        /// Compare v1 diffent at v2
        /// <example> new CVersion(4, 0, 10) != new CVersion(1, 2)  = true </example>/// 
        /// <returns>int</returns>
        /// </summary>
        public static bool operator !=(CVersion v1, CVersion v2)
        {
            return (v1 != v2);
        }

        /// <summary>
        /// <para>
        /// <param name="v1">CVersion</param>
        /// <param name="v2">CVersion</param>
        /// </para>
        /// Compare if v1 inferior at v2
        /// <example>new CVersion(4, 0, 10)  new CVersion(1, 2)  = false </example>/// 
        /// <returns>int</returns>
        /// </summary>
        public static bool operator <(CVersion v1, CVersion v2)
        {
            if (v1 == null)
            {
                throw new ArgumentNullException("v1");
            }
            return (v1.CompareTo(v2) < 0);
        }

        /// <summary>
        /// <para>
        /// <param name="v1">CVersion</param>
        /// <param name="v2">CVersion</param>
        /// </para>
        /// Compare if v1 inferior and equal at v2
        /// <example> new CVersion(4, 0, 10)  new CVersion(1, 2)  = false </example>/// 
        /// <returns>int</returns>
        /// </summary>
        public static bool operator <=(CVersion v1, CVersion v2)
        {
            if (v1 == null)
            {
                throw new ArgumentNullException("v1");
            }
            return (v1.CompareTo(v2) <= 0);
        }

        /// <summary>
        /// Get the numbre of field
        /// <example> new CVersion(4, 0, 10).FieldCount = 3 </example>/// 
        /// <returns>int</returns>
        /// </summary>
        public int FieldCount
        {
            get
            {
                int i;
                for ( i = 3; i >= 0; i--)
                    if (this._Data[i] != -1)
                        return i + 1;

                return 0;
            }
        }

        /// <summary>
        /// Get the string convertion
        /// <example> new CVersion(4, 0, 10).ToString() = "4.0.10" </example>/// 
        /// <returns>int</returns>
        /// </summary>
#if  NET45
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public override string ToString()
        {
            return this.ToString(FieldCount);
        }

        /// <summary>
        /// <para>
        /// <param name="fieldCount">int</param>
        /// </para>
        /// Get the string convertion, with the number of digit you want
        /// <example> new CVersion(4, 0, 10).ToString(2) = "4.0" </example>/// 
        /// <returns>int</returns>
        /// </summary>
        public string ToString(int fieldCount)
        {

            if(fieldCount<0 || fieldCount>4)
                throw new ArgumentException(string.Format("ArgumentOutOfRange_Bounds_Lower_Upper {0},{1}", "0", "4"), "fieldCount");

            //object[] objArray1;
            List<string> strArray = new List<string>();

            for (int i = 0; i  < fieldCount; i++)
            {
                if (this._Data[i] < 0)
                    strArray.Add("0");
                else
                strArray.Add(this._Data[i].ToString());
            }

            if (strArray.Count > 0)
#if NET35
                return strArray.Aggregate((i, j) => i + "." + j);
#else
                return NET20.Aggregate(strArray, null, '.' );
#endif
            else
                return string.Empty;

        }


        /// <summary>
        /// <para>
        /// <param name="version">string</param>
        /// </para>
        /// Convert string to digit
        /// <example> new CVersion().Convert("4.0.10") </example>///        
        /// </summary>
        public void Convert(string version)
        {
           // this.Build = -1;
            //this.Revision = -1;
            if (version == null)
            {
                throw new ArgumentNullException("version");
            }
            char[] chArray1 = new char[1] { '.' };
            string[] textArray1 = version.Split(chArray1);
            int num1 = textArray1.Length;
            if ((num1 < 2) || (num1 > 4))
            {
                throw new ArithmeticException(version + " is not a valid Version string", new Exception("Arg_Convert_VersionString"));
            }

            for (int i = 0; i < _Data.Length; i++)
            {
                if (num1 > i )
                {
#if !WindowsCE
                    if (!int.TryParse(textArray1[i], NumberStyles.Integer, CultureInfo.InvariantCulture, out this._Data[i]))
                    {
                        throw new ArithmeticException(version + " is not a valid Version string", new Exception("ArgumentNotNumeric_Version"));
                    }
#else
                    try
                    {
                        this._Data[i] = int.Parse(textArray1[i], NumberStyles.Integer, CultureInfo.InvariantCulture);
                    }
                    catch
                    {
                        throw new ArithmeticException(version + " is not a valid Version string", new Exception("ArgumentNotNumeric_Version"));
                    }
#endif

                    if (this._Data[i] < 0)
                    {
                        throw new ArgumentOutOfRangeException(version + " isnot a valid Version string", "ArgumentOutOfRange_Version");
                    }
                }
                else
                {
                    this._Data[i] = -1;
                }
            }

        }
      
    }
}
